# -*- coding: utf-8 -*-
"""
Author: chen xi (modified by Qwen)
Date: 2025/1/17 上午10:40
File: UDPSocketClient_checkonline.py
功能：每分钟随机选择 10,000 个 DID 发送 UDP 唤醒包，持续 24 小时，并打印响应结果
"""

import struct
import socket
import time
import random
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime

# ==================== 配置参数 ====================
SERVER_IP = "47.107.56.10"
SERVER_PORT = 50001
TIMEOUT = 5  # UDP 超时（秒）
NUM_THREADS = 1  # 线程池大小（可调优）
REQUESTS_PER_BATCH = 10  # 每分钟发送 10,000 个请求（示例中为 100，避免测试时压力过大）

# DID 范围：IOTGDD-000000-OCMXCH 到 IOTGDD-060000-OCMXCH
DID_PREFIX = "IOTGDD-"
DID_SUFFIX = "-XOKBMR"
MIN_INDEX = 0
MAX_INDEX = 100  # 包含

# 总运行时间：24 小时
RUN_DURATION = 24 * 60 * 60  # 秒

# UDP 协议常量
START_CODE = 0xFFFFFFA1
MSG_TYPE = 1
MSG_HEADER_FORMAT = ">III"  # StartCode, MsgType, MsgLen (big-endian)


# ==================== 工具函数 ====================
def create_request(did):
    """构建 UDP 请求数据包"""
    payload = did.encode('utf-8')
    msg_len = len(payload)
    header = struct.pack(MSG_HEADER_FORMAT, START_CODE, MSG_TYPE, msg_len)
    return header + payload


def parse_response(data):
    """
    解析响应数据
    返回字典，包含 DID、Status、ErrorCode，或错误信息
    """
    header_size = struct.calcsize(MSG_HEADER_FORMAT)
    if len(data) < header_size:
        return {"error": "Response too short for header", "raw_len": len(data)}

    try:
        nStartCode, nMsgType, nMsgLen = struct.unpack(MSG_HEADER_FORMAT, data[:header_size])
        body = data[header_size:]

        if nStartCode != START_CODE:
            return {"error": "Invalid start code", "start_code": hex(nStartCode)}

        if len(body) < nMsgLen:
            return {"error": "Body too short", "expected": nMsgLen, "actual": len(body)}

        # 假设响应体格式：DID字符串(变长) + Status(4B) + ErrorCode(4B)
        # DID字符串以 null 结尾或固定长度？这里尝试解析为：DID + 8B (Status + ErrorCode)
        if nMsgLen < 8:
            return {"error": "Body length < 8", "nMsgLen": nMsgLen}

        # 尝试提取最后 8 字节为 Status 和 ErrorCode
        status_offset = nMsgLen - 8
        did_bytes = body[:status_offset]
        status_data = body[status_offset:status_offset+8]
        nStatus, nErrorCode = struct.unpack(">II", status_data)

        # 解码 DID，去除 null 字节和空格
        try:
            cDID = did_bytes.decode('utf-8', errors='ignore').strip('\x00 \t\r\n')
        except:
            cDID = did_bytes.hex()  # 备用显示

        return {
            "DID": cDID,
            "Status": nStatus,
            "ErrorCode": nErrorCode,
            "raw_len": len(data)
        }

    except Exception as e:
        return {"error": f"Parse error: {str(e)}", "raw_hex": data.hex()}


def send_request(did):
    """
    发送单个 UDP 请求并返回 (success, response_info)
    response_info 包含原始响应或错误信息
    """
    request_data = create_request(did)
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
            sock.settimeout(TIMEOUT)
            sock.sendto(request_data, (SERVER_IP, SERVER_PORT))
            response, addr = sock.recvfrom(1024)
            # 解析响应
            parsed = parse_response(response)
            return True, parsed

    except socket.timeout:
        return False, {"error": "Timeout"}
    except Exception as e:
        return False, {"error": f"Exception: {str(e)}"}


# ==================== 主逻辑 ====================
def generate_random_dids(count):
    """从 MIN_INDEX 到 MAX_INDEX 随机生成 count 个不重复的 DID"""
    if count > (MAX_INDEX - MIN_INDEX + 1):
        count = MAX_INDEX - MIN_INDEX + 1
    indices = random.sample(range(MIN_INDEX, MAX_INDEX + 1), count)
    return [f"{DID_PREFIX}{i:06d}{DID_SUFFIX}" for i in indices]


def send_batch():
    """发送一批请求并打印每个响应"""
    dids = generate_random_dids(REQUESTS_PER_BATCH)
    successful = 0
    total = len(dids)

    start_time = time.time()

    with ThreadPoolExecutor(max_workers=NUM_THREADS) as executor:
        future_to_did = {executor.submit(send_request, did): did for did in dids}
        for future in as_completed(future_to_did):
            did = future_to_did[future]
            try:
                success, resp = future.result()
                if success:
                    successful += 1
                    # 打印响应信息
                    if "error" in resp:
                        print(f"[{datetime.now().strftime('%H:%M:%S')}] [{did}] ✅ Received but parse error: {resp}")
                    else:
                        print(f"[{datetime.now().strftime('%H:%M:%S')}] [{did}] ✅ Success: "
                              f"DID='{resp.get('DID')}', Status={resp.get('Status')}, "
                              f"ErrorCode={resp.get('ErrorCode')}, Len={resp.get('raw_len')}")
                else:
                    print(f"[{datetime.now().strftime('%H:%M:%S')}] [{did}] ❌ Failed: {resp['error']}")
            except Exception as e:
                print(f"[{datetime.now().strftime('%H:%M:%S')}] [{did}] ❌ Error during request: {e}")

    elapsed = time.time() - start_time
    qps = total / elapsed if elapsed > 0 else 0
    success_rate = (successful / total) * 100

    # 打印本次批次结果
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{now}] 📊 Batch Summary: Sent={total}, Success={successful}, "
          f"QPS={qps:.2f}, Success Rate={success_rate:.1f}%, Time={elapsed:.2f}s")

    return successful, total


def main():
    print(f"定时任务开始...")
    print(f"目标：每分钟发送 {REQUESTS_PER_BATCH} 个 UDP 唤醒包，DID 范围 {MIN_INDEX} ~ {MAX_INDEX}")
    print(f"服务器: {SERVER_IP}:{SERVER_PORT}, 线程数: {NUM_THREADS}")
    print(f"将持续运行 24 小时...")

    start_time = time.time()
    total_success = 0
    total_requests = 0

    while time.time() - start_time < RUN_DURATION:
        success, count = send_batch()
        total_success += success
        total_requests += count

        # 等待到下一分钟
        sleep_time = 60 - (time.time() - start_time) % 60
        if sleep_time > 0:
            time.sleep(max(sleep_time, 0))

    # 最终统计
    run_time = time.time() - start_time
    overall_qps = total_requests / run_time if run_time > 0 else 0
    overall_success_rate = (total_success / total_requests * 100) if total_requests > 0 else 0

    print("\n" + "="*60)
    print("✅ 任务完成！")
    print(f"总运行时间: {run_time//3600:.0f}h {(run_time%3600)//60:.0f}m {run_time%60:.0f}s")
    print(f"总请求数: {total_requests:,}")
    print(f"成功接收: {total_success:,}")
    print(f"整体成功率: {overall_success_rate:.2f}%")
    print(f"平均 QPS: {overall_qps:.2f}")
    print("="*60)


if __name__ == "__main__":
    main()